#!/usr/bin/env python
# _*_ coding:utf-8 _*_
#
# Copyright(c) http://m.ecpark.cn/
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import subprocess
import logging
import time
import os
import sys

basedir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, basedir)

log = logging.getLogger(__name__)

formatter = logging.basicConfig(
    level=logging.CRITICAL,
    format=
    '%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
    datefmt='%a, %d %b %Y %H:%M:%S')

try:
    from rabbitmq_utils import MySQLBackupStartupInfoIntoRabbitMQ
    from rabbitmq_utils import MySQLBackupFinishedInfoIntoRabbitMQ
    from os_utils import get_file_size
    from os_utils import get_file_last_modified_time
    from os_utils import get_file_last_line
    from os_utils import judge_backup_log_file_ok
    from os_utils import file_md5_calc
    from os_utils import get_xtrabackup_command
    from os_utils import make_dir
except ImportError as e:
    from rabbitmq_utils import MySQLBackupStartupInfoIntoRabbitMQ
    from rabbitmq_utils import MySQLBackupFinishedInfoIntoRabbitMQ
    from os_utils import get_file_size
    from os_utils import get_file_last_modified_time
    from os_utils import get_file_last_line
    from os_utils import judge_backup_log_file_ok
    from os_utils import file_md5_calc
    from os_utils import get_xtrabackup_command
    from os_utils import make_dir

    log.exception(str(e))


def get_now_time():
    return time.localtime(time.time())


def get_backup_file_name(mysql_host, mysql_port, compress_stream,
                         obd_time_now):
    """
    拿备份文件名,日志名
    :param mysql_host:
    :param mysql_port:
    :param compress_stream:
    :param obd_time_now:
    :return:
    """
    now_time = time.strftime('%y%m%d%H%M', obd_time_now)
    if compress_stream == 'tar':
        file_postfix = '.gz'
    elif compress_stream == 'xb':
        file_postfix = ''
    else:
        file_postfix = ''
        log.error('compress_stream error({}), neither tar or xb. '.format(
            compress_stream))

    backup_file_name = "{host}_{port}_{compress_stream}_{now_time}.{compress_stream}" \
                       "{file_postfix}".format(host=mysql_host,
                                               port=mysql_port,
                                               now_time=now_time,
                                               compress_stream=compress_stream,
                                               file_postfix=file_postfix)
    backup_log_file_name = "{host}_{port}_{now_time}.log".format(
        host=mysql_host, port=mysql_port, now_time=now_time)
    return backup_file_name, backup_log_file_name


# run_cmd(sys_command)
def run_cmd(cmd):
    print(cmd)
    """
    执行命令并发送初始信息给mq
    :param cmd: 执行的备份命令
    :param mysql_backup_startup_info_into_rabbitmq: 备份信息实例
    :return:
    """
    p = subprocess.Popen(cmd,
                         shell=True,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT)
    pid = p.pid
    mysql_backup_startup_info_into_rabbitmq.set_pid_info(pid=pid)
    mysql_backup_startup_info_into_rabbitmq.backup_startup_publish()
    result = p.stdout.read()
    return_code = p.wait()
    return return_code, result


def get_backup_command(backup_user, backup_password, mysql_base_path,
                       **kwargs):
    """
    拿备份命令
    :param backup_user:
    :param backup_password:
    :param mysql_base_path:
    :param kwargs:
    :return:
    """
    compress_stream = kwargs.get('compress_stream')
    backup_file_name = kwargs.get('backup_file_name')
    backup_log_file_name = kwargs.get('backup_log_file_name')
    backup_dir = kwargs.get('backup_dir')
    mysql_port = kwargs.get('mysql_port')

    make_dir(backup_dir)

    mysql_install_path = "{mysql_base_path}{port}".format(
        mysql_base_path=mysql_base_path, port=mysql_port)
    xtrabackup_path = '{basedir}/xtrabackup/bin'.format(basedir=basedir)
    innobackupex_command = get_xtrabackup_command(
        xtrabackup_path=xtrabackup_path,
        xtrabackup_command_name='innobackupex')

    ftwrl_wait_timeout = kwargs.get('ftwrl_wait_timeout')
    ftwrl_wait_threshold = kwargs.get('ftwrl_wait_threshold')
    ftwrl_timeout_string = " --ftwrl-wait-timeout={ftwrl_wait_timeout} " \
                           " --ftwrl-wait-threshold={ftwrl_wait_threshold} ".format(
        ftwrl_wait_timeout=ftwrl_wait_timeout, ftwrl_wait_threshold=ftwrl_wait_threshold)

    if compress_stream == 'tar':
        backup_command = ' {innobackupex_command} --defaults-file={mysql_install_path}/my.cnf ' \
                         ' --user={backup_user}   --slave_info {ftwrl_timeout_string}  ' \
                         '--password="{backup_password}" --socket={mysql_install_path}/mysql.sock --port={port}  ' \
                         ' --stream={compress_stream} {backup_dir}/ ' \
                         '2>{backup_dir}/{backup_log_file_name} ' \
                         '| gzip 1>{backup_dir}/{backup_file_name}'.format(port=mysql_port,
                                                                           backup_user=backup_user,
                                                                           backup_password=backup_password,
                                                                           compress_stream=compress_stream,
                                                                           backup_dir=backup_dir,
                                                                           ftwrl_timeout_string=ftwrl_timeout_string,
                                                                           mysql_install_path=mysql_install_path,
                                                                           backup_log_file_name=backup_log_file_name,
                                                                           backup_file_name=backup_file_name,
                                                                           innobackupex_command=innobackupex_command
                                                                           )
    elif compress_stream == 'xb':
        backup_command = ' {innobackupex_command} --defaults-file={mysql_install_path}/my.cnf ' \
                         ' --user={backup_user}   --slave_info  {ftwrl_timeout_string} ' \
                         '--password="{backup_password}" --socket={mysql_install_path}/mysql.sock --port={port}  ' \
                         ' --stream=xbstream {backup_dir}/ --compress --compress-threads=2  --parallel=2 ' \
                         ' 2>{backup_dir}/{backup_log_file_name} ' \
                         '| pigz -7 -p 2 1>{backup_dir}/{backup_file_name}'.format(port=mysql_port,
                                                                                   backup_user=backup_user,
                                                                                   backup_password=backup_password,
                                                                                   compress_stream=compress_stream,
                                                                                   backup_dir=backup_dir,
                                                                                   ftwrl_timeout_string=ftwrl_timeout_string,
                                                                                   mysql_install_path=mysql_install_path,
                                                                                   backup_log_file_name=backup_log_file_name,
                                                                                   backup_file_name=backup_file_name,
                                                                                   innobackupex_command=innobackupex_command
                                                                                   )
    else:
        backup_command = ""
        log.error('compress_stream error({}), neither tar or xb. '.format(
            compress_stream))
    return backup_command


def get_backup_startup_info(mysql_host, mysql_port, compress_stream_mod,
                            backup_base_dir):
    now_time = get_now_time()
    backup_file_name, backup_log_file_name = get_backup_file_name(
        mysql_host=mysql_host,
        mysql_port=mysql_port,
        compress_stream=compress_stream_mod,
        obd_time_now=now_time)
    start_time = time.strftime('%Y-%m-%d %H:%M:%S', now_time)
    procedure_process = 1
    backup_dir = "{backup_base_dir}/backup{port}".format(
        backup_base_dir=backup_base_dir, port=mysql_port)
    return_dict = {
        'mysql_host': mysql_host,
        'mysql_port': mysql_port,
        'backup_file_name': backup_file_name,
        'backup_log_file_name': backup_log_file_name,
        'backup_dir': backup_dir,
        'start_time': start_time,
        'compress_stream_mod': compress_stream_mod,
        'procedure_process': procedure_process
    }
    return return_dict


def get_backup_finished_info(backup_startup_info, return_code, exec_result):
    """
    根据备份开始时的信息，拿备份结束后信息
    :param backup_startup_info:
    :param return_code:
    :param exec_result:
    :return:
    """
    backup_finish_info = {}

    mysql_host = backup_startup_info.get('mysql_host')
    mysql_port = backup_startup_info.get('mysql_port')
    backup_file_name = backup_startup_info.get('backup_file_name')
    backup_log_file_name = backup_startup_info.get('backup_log_file_name')
    backup_dir = backup_startup_info.get('backup_dir')
    compress_stream_mod = backup_startup_info.get('compress_stream_mod')
    start_time = backup_startup_info.get('start_time')
    procedure_process = 0
    backup_finish_info.update({
        'mysql_host': mysql_host,
        'mysql_port': mysql_port,
        'backup_file_name': backup_file_name,
        'backup_log_file_name': backup_log_file_name,
        'backup_dir': backup_dir,
        'compress_stream_mod': compress_stream_mod,
        'procedure_process': procedure_process,
        'start_time': start_time
    })

    backup_dir = backup_startup_info.get('backup_dir')
    backup_file_name = backup_startup_info.get('backup_file_name')
    backup_log_file_name = backup_startup_info.get('backup_log_file_name')
    backup_file_md5 = file_md5_calc('{backup_dir}/{backup_file_name}'.format(
        backup_dir=backup_dir, backup_file_name=backup_file_name))
    if isinstance(exec_result, bytes):
        try:
            # exec_result = exec_result.decode("utf-8")
            exec_result = ""
        except UnicodeDecodeError as e:
            pass
    # print(exec_result)
    backup_file = "{backup_dir}/{backup_file_name}".format(
        backup_dir=backup_dir, backup_file_name=backup_file_name)
    backup_log_file = "{backup_dir}/{backup_log_file_name}".format(
        backup_dir=backup_dir, backup_log_file_name=backup_log_file_name)

    backup_file_size = get_file_size(backup_file)
    backup_file_last_modified_time = get_file_last_modified_time(backup_file)
    backup_log_file_last_line = get_file_last_line(backup_log_file)
    is_backup_ok = judge_backup_log_file_ok(backup_log_file_last_line)
    backup_finish_info.update({
        'backup_file_size': backup_file_size,
        'backup_file_last_modified_time': backup_file_last_modified_time,
        'backup_log_file_last_line': backup_log_file_last_line,
        'return_code': return_code,
        'is_backup_ok': is_backup_ok,
        'file_md5': backup_file_md5
    })

    return backup_finish_info


def mysql_backup_function(**kwargs):
    """
    mysql 备份入口函数
    :param kwargs:
    :return:
    """
    host = kwargs.get("host")
    port = kwargs.get("port")
    compress_stream = kwargs.get("compress_stream")
    backup_user = kwargs.get("backup_user")
    backup_password = kwargs.get("backup_password")
    backup_base_dir = kwargs.get("backup_base_dir")
    mysql_base_path = kwargs.get("mysql_base_path")
    ftwrl_wait_timeout = kwargs.get("ftwrl_wait_timeout")
    ftwrl_wait_threshold = kwargs.get("ftwrl_wait_threshold")

    backup_startup_info = get_backup_startup_info(
        mysql_host=host,
        mysql_port=port,
        compress_stream_mod=compress_stream,
        backup_base_dir=backup_base_dir)
    backup_command = get_backup_command(compress_stream=compress_stream,
                                        backup_user=backup_user,
                                        backup_password=backup_password,
                                        mysql_base_path=mysql_base_path,
                                        ftwrl_wait_timeout=ftwrl_wait_timeout,
                                        ftwrl_wait_threshold=ftwrl_wait_threshold,
                                        **backup_startup_info)
    log.info(backup_command)
    try:
        # mysql_backup_startup_info_into_rabbitmq = MySQLBackupStartupInfoIntoRabbitMQ(
        # )
        # mysql_backup_startup_info_into_rabbitmq.set_backup_startup_info(
        #     backup_startup_info)
        return_code, exec_result = run_cmd(
            backup_command)
        log.info(exec_result)
        # mysql_backup_finished_info = get_backup_finished_info(
            # backup_startup_info, return_code, exec_result)
        # mysql_backup_finished_info_into_rabbitmq = MySQLBackupFinishedInfoIntoRabbitMQ(
        # )
        # mysql_backup_finished_info_into_rabbitmq.set_backup_finished_info(
            # backup_finished_info=mysql_backup_finished_info)
        # mysql_backup_finished_info_into_rabbitmq.backup_startup_publish()
    except Exception as e:
        log.exception(str(e))
